#include "llcc68_drive.h"

extern EventGroupHandle_t xLLCC68EventGroup;

void llcc68_rx(void)
{
  llcc68_set_standby(NULL, LLCC68_STANDBY_CFG_RC);
  //llcc68_set_pkt_type(NULL, LLCC68_PKT_TYPE_LORA);

  llcc68_set_rx_with_timeout_in_rtc_step(NULL, LLCC68_MAX_TIMEOUT_IN_RTC_STEP);
}

void llcc68_send(uint8_t *buffer, uint8_t size)
{
  //llcc68_set_standby(NULL, LLCC68_STANDBY_CFG_RC);
  llcc68_set_tx_cfg(size);
  llcc68_clear_irq_status(NULL, LLCC68_IRQ_ALL);
  llcc68_write_buffer(NULL, 0x00, buffer, size);

  llcc68_set_tx(NULL, 1000);
}

void llcc68_set_tx_cfg(uint8_t pld_len_in_bytes)
{
  // MODEM_LORA
  llcc68_stop_timer_on_preamble(NULL, false);
  llcc68_set_lora_symb_nb_timeout(NULL, 0);

  //llcc68_set_standby(NULL, LLCC68_STANDBY_CFG_RC);
  llcc68_set_pkt_type(NULL, LLCC68_PKT_TYPE_LORA);

  llcc68_mod_params_lora_t llcc68_mod_params_lora;
  llcc68_mod_params_lora.sf = LORA_SPREADING_FACTOR;
  llcc68_mod_params_lora.bw = LORA_BANDWIDTH;
  llcc68_mod_params_lora.cr = LORA_CODINGRATE;
  llcc68_mod_params_lora.ldro = 0x00;
  llcc68_set_lora_mod_params(NULL, &llcc68_mod_params_lora);

  llcc68_pkt_params_lora_t llcc68_pkt_params_lora;
  llcc68_pkt_params_lora.preamble_len_in_symb = LORA_PREAMBLE_LENGTH;
  llcc68_pkt_params_lora.header_type = LORA_FIX_LENGTH_PAYLOAD_ON;
  llcc68_pkt_params_lora.pld_len_in_bytes = pld_len_in_bytes;
  llcc68_pkt_params_lora.crc_is_on = true;
  llcc68_pkt_params_lora.invert_iq_is_on = LORA_IQ_INVERSION_ON;
  llcc68_set_lora_pkt_params(NULL, &llcc68_pkt_params_lora);

  llcc68_set_dio_irq_params(NULL,
                            LLCC68_IRQ_ALL,
                            LLCC68_IRQ_ALL,
                            LLCC68_IRQ_NONE,
                            LLCC68_IRQ_NONE);
  llcc68_set_rx_with_timeout_in_rtc_step(NULL, LLCC68_MAX_TIMEOUT_IN_RTC_STEP);
}

void llcc68_chip_init(void)
{
  llcc68_gpio_init();
  llcc68_spi_init();
  llcc68_reset(NULL);

  llcc68_set_standby(NULL, LLCC68_STANDBY_CFG_RC);
  llcc68_set_dio2_as_rf_sw_ctrl(NULL, true);
  llcc68_cal(NULL, LLCC68_CAL_ALL);

  llcc68_set_reg_mode(NULL, LLCC68_REG_MODE_DCDC);
  llcc68_set_buffer_base_address(NULL, 0X00, 0X00);

  llcc68_pa_cfg_params_t llcc68_pa_cfg_params;
  llcc68_pa_cfg_params.pa_duty_cycle = 0x04;
  llcc68_pa_cfg_params.hp_max = 0x07;
  llcc68_pa_cfg_params.device_sel = 0x00;
  llcc68_pa_cfg_params.pa_lut = 0x01;
  llcc68_set_pa_cfg(NULL, &llcc68_pa_cfg_params);
  llcc68_set_tx_params(NULL, 22, LLCC68_RAMP_200_US);

  llcc68_cal_img(NULL,0x6b,0x6f);
  llcc68_set_rf_freq(NULL, LORA_FRE);
}

void llcc68_spi_init(void)
{
  dma_init_type dma_init_struct;

  // DMA
  crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
  dma_reset(DMA1_CHANNEL1);
  dma_reset(DMA1_CHANNEL2);
  dma_default_para_init(&dma_init_struct);
  dmamux_enable(DMA1, TRUE);
  dmamux_init(DMA1MUX_CHANNEL1, DMAMUX_DMAREQ_ID_SPI1_RX);
  dmamux_init(DMA1MUX_CHANNEL2, DMAMUX_DMAREQ_ID_SPI1_TX);

  dma_init_struct.buffer_size = 0;
  dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
  dma_init_struct.memory_base_addr = (uint32_t)0x0;
  dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
  dma_init_struct.memory_inc_enable = TRUE;
  dma_init_struct.peripheral_base_addr = (uint32_t)(&SPI1->dt);
  dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
  dma_init_struct.peripheral_inc_enable = FALSE;
  dma_init_struct.priority = DMA_PRIORITY_LOW;
  dma_init_struct.loop_mode_enable = FALSE;
  dma_init(DMA1_CHANNEL1, &dma_init_struct);

  dma_init_struct.buffer_size = 0;
  dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
  dma_init_struct.memory_base_addr = (uint32_t)0x0;
  dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
  dma_init_struct.memory_inc_enable = TRUE;
  dma_init_struct.peripheral_base_addr = (uint32_t)(&SPI1->dt);
  dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
  dma_init_struct.peripheral_inc_enable = FALSE;
  dma_init_struct.priority = DMA_PRIORITY_LOW;
  dma_init_struct.loop_mode_enable = FALSE;
  dma_init(DMA1_CHANNEL2, &dma_init_struct);

  spi_init_type spi_init_struct;
  crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);
  spi_i2s_reset(SPI1);
  spi_default_para_init(&spi_init_struct);
  spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
  spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
  spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_16;
  spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
  spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
  spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_LOW;
  spi_init_struct.clock_phase = SPI_CLOCK_PHASE_1EDGE;
  spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;
  spi_init(SPI1, &spi_init_struct);
  spi_i2s_dma_transmitter_enable(SPI1, TRUE);
  spi_i2s_dma_receiver_enable(SPI1, TRUE);
  spi_enable(SPI1, TRUE);
}

void llcc68_gpio_init(void)
{
  gpio_init_type gpio_initstructure;

  crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);

  // PB3 SPI1_SCK PB4 SPI1_MISO PB5 SPI1_MOSI
  gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
  gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_initstructure.gpio_pins = GPIO_PINS_3 | GPIO_PINS_4 | GPIO_PINS_5;
  gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
  gpio_init(GPIOB, &gpio_initstructure);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE3, GPIO_MUX_5);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE4, GPIO_MUX_5);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE5, GPIO_MUX_5);

  // PA15 SPI1_CS
  gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
  gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_initstructure.gpio_pins = GPIO_PINS_15;
  gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
  gpio_init(GPIOA, &gpio_initstructure);
  SPI1_CS_HIGH();

  // PC2 BUSY PC3 DIO1
  gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
  gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_initstructure.gpio_pins = GPIO_PINS_2 | GPIO_PINS_3;
  gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
  gpio_init(GPIOC, &gpio_initstructure);

  crm_periph_clock_enable(CRM_SCFG_PERIPH_CLOCK, TRUE);
  scfg_exint_line_config(SCFG_PORT_SOURCE_GPIOC, SCFG_PINS_SOURCE3);

  exint_init_type exint_init_struct;
  exint_default_para_init(&exint_init_struct);
  exint_init_struct.line_enable = TRUE;
  exint_init_struct.line_mode = EXINT_LINE_INTERRUPUT;
  exint_init_struct.line_select = EXINT_LINE_3;
  exint_init_struct.line_polarity = EXINT_TRIGGER_RISING_EDGE;
  exint_init(&exint_init_struct);

  nvic_irq_enable(EXINT3_IRQn, 5, 0);

  // PA1 RST
  gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
  gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_initstructure.gpio_pins = GPIO_PINS_1;
  gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
  gpio_init(GPIOA, &gpio_initstructure);
  LLCC68_RST_HIGH();
}

void EXINT3_IRQHandler(void)
{
  if(exint_flag_get(EXINT_LINE_3) != RESET)
  {
    BaseType_t xHigherPriorityTaskWoken, xResult;

    xHigherPriorityTaskWoken = pdFALSE;

    xResult = xEventGroupSetBitsFromISR(
                xLLCC68EventGroup,
                DIO_DONE_BIT,
                &xHigherPriorityTaskWoken);

    if( xResult != pdFAIL )
    {
      portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    }
    exint_flag_clear(EXINT_LINE_3);
  }
}